﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using VisionCloud.Api.Attributes;

namespace VisionCloud.Web.Middleware
{
    /// <summary>
    /// Log日志中间件
    /// </summary>
    public class LogMiddleware
    {
        private readonly RequestDelegate next;
        private readonly ILogger<LogMiddleware> logger;

        public LogMiddleware(RequestDelegate next, ILogger<LogMiddleware> logger)
        {
            this.next = next;
            this.logger = logger;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            HttpRequest request = httpContext.Request;
            HttpResponse response = httpContext.Response;
            var originBody = response.Body;
            StreamReader responseStreamReader = null;
            using (var memoryStream = new MemoryStream())
            {
                try
                {
                    httpContext.Request.EnableBuffering();
                    var logAttribute = (LogAttribute)httpContext.GetEndpoint()?.Metadata.FirstOrDefault(x => x.GetType() == typeof(LogAttribute));
                    if (logAttribute == null)
                    {
                        await next.Invoke(httpContext);
                        return;
                    }
                    if (logAttribute.NeedResponseBody)
                    {
                        response.Body = memoryStream;
                    }
                    await next.Invoke(httpContext);

                    string requestBody = string.Empty;
                    string responseBody = string.Empty;

                    if (logAttribute.NeedRequestBody)
                    {
                        string requestUrl = $"{request.Path.Value}{request.QueryString.Value}";
                        if (request.HasFormContentType)
                        {
                            IFormCollection fromColl = request.Form;
                            requestBody += "{";
                            foreach (var item in fromColl)
                            {
                                requestBody += $"{item.Key}:{item.Value},";
                            }
                            requestBody += requestBody.TrimEnd(',') + "}";
                        }
                        else
                        {
                            if (request.Body.CanSeek)
                                request.Body.Seek(0, SeekOrigin.Begin);
                            using StreamReader requestStreamReader = new StreamReader(request.Body);
                            requestBody = await requestStreamReader.ReadToEndAsync();
                        }
                    }
                    if (logAttribute.NeedResponseBody)
                    {

                        memoryStream.Seek(0, SeekOrigin.Begin);
                        responseStreamReader = new StreamReader(memoryStream);
                        responseBody = await responseStreamReader.ReadToEndAsync();
                    }

                    string log = JsonConvert.SerializeObject(new
                    {
                        path = request.Path,
                        requestBody,
                        responseBody,
                    });
                    logger.LogInformation($"请求上下文日志：{log}");
                }
                catch (Exception e)
                {
                    logger.LogInformation(e, $"日志中间件出现异常，请及时处理：{e.Message}");
                }
                finally
                {
                    // 还原response流内容 && 释放资源
                    if (memoryStream.CanSeek)
                        memoryStream.Seek(0, SeekOrigin.Begin);
                    await memoryStream.CopyToAsync(originBody);
                    response.Body = originBody;
                    if (response.Body.CanSeek)
                        response.Body.Seek(0, SeekOrigin.Begin);
                    if (responseStreamReader != null)
                    {
                        responseStreamReader.Close();
                        responseStreamReader.Dispose();
                    }
                }
            }
        }
    }
}
